# -*- encoding: utf-8 -*-

import logging, re, datetime

from django.db.models import Q
from django.template.loader import render_to_string
from django.shortcuts import render_to_response
from django.http import HttpResponse

class Search(object): 
    jsontemplate = 'ejeje/search.json'
    htmltemplate = 'ejeje/index.html'
    feedtemplate = 'ejeje/search.atom'
    # colMap is required for correct sorting behavior
#  "{{row.uri|default:""|iriencode}}",
#  {{row.offentlig|yesno:"true,false"}},
#  "{{row.tittel|lower|capfirst}}",
#  {% with row.postjournal.organisasjon as org %}
#  "{{org.navn|title}}{%if not org.moder %} {{org.get_type_display}}{%else%}({{org.moder.navn|title}} {{org.moder.get_type_display}}){% endif %}",
#  {% endwith %}
#  "{{row.dato|default:""}}",
#  "{{row.sakskompleks|default:""}}",
#  "{{row.get_retning_display}}",
#  "{{row.lovreferanse|default:""|lower|capfirst}}"
    colmap = ['uri', 'offentlig', 'tittel', 'organisasjon', 'dokumentdato', 'sakskompleks', 'retning', 'lovreferanse']
    #model fields listed in searchableColumns will be used for filtering (Search)
    searchableColumns = ['tittel', 'sakskompleks', 'dokumentdato', 'arkivdato', 'lovreferanse']
    modifierfields = ['id', 'kommentar', 'lovreferanse', 'offentlig', 'refnr', 'retning', 'sakskompleks', 'tittel', 'uri', 'arkivdato', 'arkivsaksnr', 'dokumentdato', 'dokumentnr']
    qrows = 100 # how many rows in the result set - default 
    qpage = 1 # what result page
    qsidx = 5 # sort column
    qsord = 'desc' # sort order
    filtered = False
    
    def __init__(self, request, querySet, *args):
    
        self.request = request
        
        #Safety measure. If someone messes with qrows manually, we clip it to
        #the max value of 100.
        try:
            self.qrows = min(int(request.GET['rows']),100)
        except (KeyError, ValueError):
            pass
            
        try:
            self.qpage = int(request.GET['page'])
        except (KeyError, ValueError):
            pass
            
        try:
            self.qsidx = int(request.GET['sidx'])
        except ValueError:
            _sidx = request.GET['sidx'] 
            if _sidx in self.colmap:
                self.qsidx = _sidx 
        except KeyError:
            pass

        if isinstance(self.qsidx, int):
            self.orderby = self.colmap[self.qsidx-1]
        else:
            self.orderby = self.qsidx
        try:
            self.qsord = request.GET['sord']
            if self.qsord.lower() in ('desc',):
                self.orderby = "-%s" % self.orderby 
        except KeyError:
            pass
            
        logging.debug("ordering results by  %s", self.orderby)    
        querySet = querySet.order_by(self.orderby)
        
        #apply filtering by value sent by user
        try:
            self.filtered = (request.GET['_search'] == 'true')
        except KeyError:
            self.filtered = False
        
        if self.filtered:
            search = self.parse_query(request.GET['search'].encode('utf-8'))
            logging.debug("searchwords: %s", search)
            for field, s in search[:]:
                logging.debug("found modifier: %s", field)
                meth = 'filter'
                if isinstance(field, basestring) and field[0] == "-": # excluding
                    field = field[1:]
                    meth = 'exclude'
                    logging.debug("excluding %s:%s", field, s)
                elif field is None and s[0] == "-": # excluding
                    s = s[1:]
                    meth = 'exclude'
                    logging.debug("excluding %s", s)
                if field in ('organisasjon', 'org'):
                    m = getattr(querySet, meth)
                    querySet = m(**{'postjournal__organisasjon__navn__icontains':s})
                elif field in ('adressat', 'saksbehandler'):
                    m = getattr(querySet, meth)
                    querySet = m(**{field+'__navn__icontains':s})
                elif field in ('offentlig', ):
                    m = getattr(querySet, meth)
                    b = 0
                    if s in ('ja','1','true'): b = 1
                    elif s in ('nei', '0', 'false'): b = 0
                    else: 
                        logging.debug("unknown bool value: %s", s)
                        continue 
                    logging.debug("%s", {field:b})
                    querySet = m(**{field:b})
                elif field == 'retning':
                    if s in ('i','u','x','s'):
                        querySet = m(retning=s.upper())
                    else:
                        logging.debug('unknown direction: %s', s)
                elif field in self.modifierfields:
                    m = getattr(querySet, meth)
                    querySet = m(**{field+'__icontains':s})
                elif field is None: 
                    outputQ = None
                    for searchableColumn in self.searchableColumns:
                        kwargz = {searchableColumn+"__icontains" : s}
                        q = Q(**kwargz)
                        if outputQ is None:
                            outputQ = q
                        else:
                            outputQ |= q
                    logging.debug(outputQ)
                    querySet = querySet.filter(outputQ)
                else:
                    logging.debug('modifier not recognised: "%s"', field) 
            
        logging.debug("%s records", querySet.count())
        endrow = self.qrows*self.qpage
        startrow = endrow - self.qrows
        #count how many records match the final criteria
        self.config = { 'totalrecords': querySet.count(),
                        'totalpages' : int(querySet.count() / self.qrows) + 1,
                        'currentpage': int(startrow / self.qrows) + 1
                       }
        
        #get the slice
        self.querySet = querySet[startrow:endrow]
        
        #record the search
        if self.filtered and request.user.is_authenticated():
            _s = request.GET['search'].encode('utf-8')
            if _s:
                logging.debug("registering search: '%s' - %s hits", _s, self.config['totalrecords'])
                rec = request.user.sok_set.create(tekst=_s, treff=self.config['totalrecords'])
            
        
    def getDatatablesJSON(self):
        #prepare the JSON with the response
        querySet = self.querySet
        config = self.config
        jstonString = render_to_string(self.jsontemplate, locals())
        return HttpResponse(jstonString, mimetype="application/javascript")        

    def getFeed(self):
        querySet = self.querySet
        try:
            query = self.request.GET["search"].encode("utf-8")
        except:
            query = ""
        orderedby = self.orderby
        now = datetime.datetime.now()
        feedString = render_to_string(self.feedtemplate, locals())
        return HttpResponse(feedString, mimetype="application/atom+xml")
        
    def getHtml(self):
        return render_to_response(self.htmltemplate, locals())

    def parse_query(self, query):
        """ saksbehandler:"Q Q" organisasjon:Q orgtype:Q adressat:Q Q -> list of query items"""
        struct = []
        rex = re.compile("""((-?\w+):)?(("([^"]+)")|('([^']+)')|[^\ ]+)""")
        while len(query) > 0:
            logging.debug("loking at %s", query)
            s = rex.search(query.lower())
            if s is None:
                break
            field = s.group(2)
            subquery = s.group(3).lower()
            if s.group(7) is not None:
                subquery = s.group(7).lower()
            elif s.group(5) is not None:
                subquery = s.group(5).lower()
            struct.append( (field, subquery) )
            try:
                query = query[s.end(0):]
            except IndexError: # end of string
                break
        logging.debug("found searches: %s", struct)
        return struct
        
        
    def parse_datestring(self, string):
        """ [<|>] 2007 | 2007-12 | 2007-12-01 [-<date> ] -> storm expr"""
        rex = re.compile(r"(<|>)?(\d{4})(\.(\d{2})(\.(\d{2}))?)?(-(\d{4})(\.(\d{2})(\.(\d{2}))?)?)?")
        s = rex.match(string)
        if not s:
            return None
        logging.debug("datestfing: %s", s.groups())
        struct = { 'comp': s.group(1),
                   #'date1': '-'.join([d for d in (s.group(2), s.group(4), s.group(6)) if d]),
                   #'date2': '-'.join([d for d in (s.group(8), s.group(10), s.group(12)) if d]),
                   'date1': datetime.date(*([int(d or 1) for d in (s.group(2), s.group(4), s.group(6))])),
                   'date2': None,
                 }
        if s.group(8) is not None: # second date also
            struct['date2'] = datetime.date(*([int(d or 1) for d in (s.group(8), s.group(10), s.group(12))]))

        logging.debug("parsed date: %s", struct)
        return struct

